package com.zy.common.convert;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.zy.common.annotation.ExcelOtherTableFormat;
import com.zy.common.utils.sql.StringUtils;
import com.zy.common.utils.spring.SpringUtils;
import jodd.cache.TimedCache;
import lombok.extern.slf4j.Slf4j;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 其他表数据转换处理
 *
 * @author Lion Li
 */
@Slf4j
public class ExcelOtherTableConvert implements Converter<Object> {

    private static TimedCache<String, String> cache = new TimedCache<>(2 * 60 * 1000);

    @Override
    public Class<Object> supportJavaTypeKey() {
        return Object.class;
    }

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return null;
    }

    @Override
    public Object convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
        throw new UnsupportedOperationException();
    }

    @Override
    public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws InstantiationException, IllegalAccessException {
        if (ObjectUtil.isNull(object)) {
            return new WriteCellData<>("");
        }
        ExcelOtherTableFormat anno = getAnnotation(contentProperty.getField());
        if (anno != null ) {
            Class cls = anno.cls();
            String keyField = anno.key();
            Stream<Object> stream;
            if (object instanceof String) {
                stream = Arrays.stream(((String) object).split(anno.split()));
            } else {
                stream = Stream.of(object);
            }
            String ret = stream.map(k -> {
                String keyValue = Convert.toStr(k);
                String cacheKey = cls.getSimpleName() + "#" + keyField + "#" + keyValue;
                return cache.get(cacheKey);
            }).filter(Objects::nonNull).collect(Collectors.joining(anno.split()));
            if (StringUtils.isNotBlank(ret)) {
                return new WriteCellData<>(ret);
            }
            return new WriteCellData<>("");
        } else {
            throw new RuntimeException("没有指定@ExcelOtherTableFormat注解！");
        }
    }

    private static ExcelOtherTableFormat getAnnotation(Field field) {
        return AnnotationUtil.getAnnotation(field, ExcelOtherTableFormat.class);
    }

    /**
     * 提前缓存数据
     */
    public static <T> void preHandle(List<T> list, Class<T> clazz) {
        EntityManager entityManager = SpringUtils.getBean(EntityManager.class);
        for (Field declaredField : ClassUtil.getDeclaredFields(clazz)) {
            ExcelOtherTableFormat annotation = getAnnotation(declaredField);
            if (annotation != null) {
                declaredField.setAccessible(true);
                Class cls = annotation.cls();
                PropertyDescriptor propertyDescriptor = BeanUtil.getPropertyDescriptor(cls, annotation.key());
                Class<?> propertyType = propertyDescriptor.getPropertyType();
                List<Object> values = list.stream().flatMap(item -> {
                        Object fieldValue = BeanUtil.getFieldValue(item, declaredField.getName());
                        if (fieldValue instanceof String) {
                            return Arrays.stream(((String) fieldValue).split(annotation.split()));
                        }
                        return Stream.of(fieldValue);
                    }).map(v -> Convert.convert(propertyType, v))
                    .distinct().collect(Collectors.toList());
                CriteriaBuilder cb = entityManager.getCriteriaBuilder();
                CriteriaQuery cq = cb.createQuery(Object[].class);
                Root root = cq.from(cls);
                List<Object[]> result = entityManager.createQuery(cq
                        .multiselect(root.get(annotation.key()), root.get(annotation.label()))
                        .where(root.get(annotation.key()).in(values))
                    ).getResultList();
                for (Object[] o : result) {
                    String cacheKey = cls.getSimpleName() + "#" + annotation.key() + "#" + o[0].toString();
                    cache.put(cacheKey, o[1].toString());
                }
            }
        }
    }
}
